/*
 * LingPipe v. 4.1.0
 * Copyright (C) 2003-2011 Alias-i
 *
 * This program is licensed under the Alias-i Royalty Free License
 * Version 1 WITHOUT ANY WARRANTY, without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the Alias-i
 * Royalty Free License Version 1 for more details.
 *
 * You should have received a copy of the Alias-i Royalty Free License
 * Version 1 along with this program; if not, visit
 * http://alias-i.com/lingpipe/licenses/lingpipe-license-1.txt or contact
 * Alias-i, Inc. at 181 North 11th Street, Suite 401, Brooklyn, NY 11211,
 * +1 (718) 290-9170.
 */
package com.aliasi.tokenizer;

import com.aliasi.util.AbstractExternalizable;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;

/**
 * A {@code ModifiedTokenizerFactory} is an abstract tokenizer factory
 * that modifies a tokenizer returned by a base tokenizer factory.
 *
 * <p>The abstract method {@link #modify(Tokenizer)} implements
 * the modification.
 *
 * <h3>Serialization</h3>
 *
 * This class implements {@code Serializable}.  There are no
 * serialization methods defined, so the default serialization is
 * used.  There is a single reference to the base tokenizer factory,
 * so a subclass will be serializable if all of its member objects
 * are serializable and the base tokenizer is serializable.
 *
 * <p>It is good practice for each subclass to take completecontrol
 * over serialization using a serialization proxy implemented 
 * on top of the {@link AbstractExternalizable} base
 * class.
 * 
 * <h3>Thread Safety</h3>
 *
 * A modified tokenizer factory is thread safe if its {@code modify()}
 * method is thread safe.
 *
 * @author  Bob Carpenter
 * @version 3.8
 * @since   Lingpipe3.8
 */
public abstract class ModifiedTokenizerFactory
    implements TokenizerFactory {

    private final TokenizerFactory mFactory;

    /**
     * Construct a modified tokenizer factory with the
     * specified base factory.
     *
     * @param baseFactory Underlying tokenizer factory whose
     * tokenizers are modified.
     */
    public ModifiedTokenizerFactory(TokenizerFactory baseFactory) {
        mFactory = baseFactory;
    }

    /**
     * Return the base tokenizer factory.
     *
     * @return The base tokenizer factory.
     */
    public TokenizerFactory baseTokenizerFactory() {
        return mFactory;
    }

    /**
     * Return the tokenizer for the specified character array slice,
     * which is generated by the base tokenizer and modified with
     * the modify method.
     */
    public Tokenizer tokenizer(char[] cs, int start, int length) {
        Tokenizer tokenizer = mFactory.tokenizer(cs,start,length);
        return modify(tokenizer);
    }

    @Override
    public String toString() {
        return getClass().getName()
            + "\n  base factory=\n    " + baseTokenizerFactory().toString().replace("\n","\n    ");
    }

    /**
     * Return a modified form of the specified tokenizer.  This
     * method is used to modify the tokenizer produced by the
     * base tokenizer in a call to {@link #tokenizer(char[],int,int)}.
     *
     * @param tokenizer Tokenizer to modify.
     * @return The modified tokenizer.
     */
    abstract protected Tokenizer modify(Tokenizer tokenizer);


    static abstract class AbstractSerializer<T extends ModifiedTokenizerFactory>
        extends AbstractExternalizable {
        static final long serialVersionUID = 55645850738262892L;
        private final T mFactory;
        AbstractSerializer() {
            this(null);
        }
        AbstractSerializer(T factory) {
            mFactory = factory;
        }
        public T factory() {
            return mFactory;
        }
        public Object read(ObjectInput in)
            throws IOException, ClassNotFoundException {
            TokenizerFactory factory = (TokenizerFactory) in.readObject();
            return read(in,factory);
        }
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(mFactory.baseTokenizerFactory());
            writeExternalRest(out);
        }
        public void writeExternalRest(ObjectOutput out) throws IOException {
        }
        abstract public Object read(ObjectInput in,
                                    TokenizerFactory baseFactory)
            throws IOException, ClassNotFoundException;
    }


}
